package com.example.socket.handler;

import com.example.socket.anno.InBody;
import com.example.socket.anno.InRequest;
import com.example.socket.anno.InSession;
import com.example.socket.anno.impl.*;
import com.example.socket.codec.MessageConvertor;
import com.example.socket.core.Request;
import com.example.socket.core.Session;
import com.example.socket.exception.ParameterException;
import com.example.socket.parameter.Parameter;
import com.example.socket.parameter.ResultCallback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.helpers.FormattingTuple;
import org.slf4j.helpers.MessageFormatter;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public class ParameterBuilder {

    private static final Logger logger = LoggerFactory.getLogger(Parameter.class);

    private MessageConvertor convertor;

    public ParameterBuilder(MessageConvertor convertor) {
        this.convertor = convertor;
    }

    /**
     * 创建参数数组
     * @param method 方法
     * @return
     */
    public Parameter[] buildParameters(Method method) {
        Type[] types = method.getGenericParameterTypes();
        Parameter[] result = new Parameter[types.length];
        for (int i = 0; i < types.length; i++) {
            result[i] = valueOf(method, i);
        }
        return result;
    }

    /**
     * 创建参数对象
     * @param method 方法
     * @param index 参数下标
     * @return
     */
    @SuppressWarnings("rawtypes")
    public Parameter valueOf(Method method, int index) {
        if (index >= method.getGenericParameterTypes().length) {
            FormattingTuple message = MessageFormatter.format("参数下标[{}]超过了方法[{}]的有效下标", index, method.getName());
            logger.error(message.getMessage());
            throw new IllegalArgumentException(message.getMessage());
        }

        Type type = method.getGenericParameterTypes()[index];
        Annotation[] annotations = method.getParameterAnnotations()[index];
        for (Annotation a : annotations) {
            if (a instanceof InBody) {
                try {
                    return InBodyParameter.valueOf((InBody) a, method, index, convertor);
                } catch (Exception e) {
                    throw new ParameterException(e);
                }
            } else if (a instanceof InRequest) {
                return InRequestParameter.valueOf((InRequest) a);
            } else if (a instanceof InSession) {
                return InSessionParameter.valueOf((InSession) a);
            }
        }

        if (type instanceof Class && Session.class.isAssignableFrom((Class) type)) {
            return SessionParameter.instance;
        }
        boolean callbackParameter = (type instanceof Class && ResultCallback.class.isAssignableFrom((Class) type))
                || (type instanceof ParameterizedType && ResultCallback.class.isAssignableFrom((Class) ((ParameterizedType) type).getRawType()));
        if (callbackParameter) {
            return CallbackParameter.instance;
        }
        boolean requestParameter = (type instanceof Class && Request.class.isAssignableFrom((Class) type))
                || (type instanceof ParameterizedType && ((ParameterizedType) type).getRawType().equals(Request.class));
        if (requestParameter) {
            return RequestParameter.instance;
        }

        return BodyParameter.instance;
    }

    public void setConvertor(MessageConvertor convertor) {
        this.convertor = convertor;
    }

    public MessageConvertor getConvertor() {
        return convertor;
    }
}
